popover menu: Unify hover and focus
authorMatthias Clasen <mclasen@redhat.com>
Thu, 13 Jun 2019 00:13:21 +0000 (00:13 +0000)
committerMatthias Clasen <mclasen@redhat.com>
Thu, 13 Jun 2019 01:50:46 +0000 (21:50 -0400)
Menus traditionally don't have separate
hover and focus locations. Make the same
change here that we already did for
popover menubars: Track the active item
and set its selected state. Both keynav
and mouse change the active item.

gtk/gtkmodelbutton.c
gtk/gtkpopovermenu.c
gtk/gtkpopovermenuprivate.h [new file with mode: 0644]

index 57c333536b2d8b6aab168a89b5681d5605d9fed7..80552ec33bff886ecb7dc8897daf09852f03998d 100644 (file)
@@ -31,7 +31,7 @@
 #include "gtkstylecontext.h"
 #include "gtktypebuiltins.h"
 #include "gtkstack.h"
-#include "gtkpopover.h"
+#include "gtkpopovermenuprivate.h"
 #include "gtkintl.h"
 #include "gtkcssnodeprivate.h"
 #include "gtkcsstypesprivate.h"
@@ -41,6 +41,8 @@
 #include "gtksizegroup.h"
 #include "gtkaccellabelprivate.h"
 #include "gtkactionable.h"
+#include "gtkeventcontrollermotion.h"
+#include "gtkeventcontrollerkey.h"
 
 /**
  * SECTION:gtkmodelbutton
@@ -1127,9 +1129,75 @@ gtk_model_button_class_init (GtkModelButtonClass *class)
   gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), I_("modelbutton"));
 }
 
+static void
+enter_cb (GtkEventController *controller,
+          double              x,
+          double              y,
+          GdkCrossingMode     mode,
+          GdkNotifyType       type,
+          gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+  gboolean is;
+  gboolean contains;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  g_object_get (controller,
+                "is-pointer-focus", &is,
+                "contains-pointer-focus", &contains,
+                NULL);
+
+  if (popover && (is || contains))
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+}
+
+static void
+leave_cb (GtkEventController *controller,
+          GdkCrossingMode     mode,
+          GdkNotifyType       type,
+          gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+  gboolean is;
+  gboolean contains;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  g_object_get (controller,
+                "is-pointer-focus", &is,
+                "contains-pointer-focus", &contains,
+                NULL);
+
+  if (popover && !(is || contains))
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), NULL);
+}
+
+static void
+focus_in_cb (GtkEventController *controller,
+             GdkCrossingMode     mode,
+             GdkNotifyType       type,
+             gpointer            data)
+{
+  GtkWidget *target;
+  GtkWidget *popover;
+
+  target = gtk_event_controller_get_widget (controller);
+  popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU);
+
+  if (popover)
+    gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target);
+}
+
 static void
 gtk_model_button_init (GtkModelButton *button)
 {
+  GtkEventController *controller;
+
   button->role = GTK_BUTTON_ROLE_NORMAL;
   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
   button->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
@@ -1161,6 +1229,15 @@ gtk_model_button_init (GtkModelButton *button)
   gtk_widget_hide (button->start_indicator);
   gtk_widget_hide (button->end_indicator);
   update_node_ordering (button);
+
+  controller = gtk_event_controller_motion_new ();
+  g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), NULL);
+  g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), NULL);
+  gtk_widget_add_controller (GTK_WIDGET (button), controller);
+
+  controller = gtk_event_controller_key_new ();
+  g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), NULL);
+  gtk_widget_add_controller (GTK_WIDGET (button), controller);
 }
 
 /**
index 93a0ba89532908c497ce51ef7b85280ec2ea348b..856b017cb51ae3889f6c8c14bd743057e2e5e873 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "config.h"
 #include "gtkpopovermenu.h"
+#include "gtkpopovermenuprivate.h"
+
 #include "gtkstack.h"
 #include "gtkstylecontext.h"
 #include "gtkintl.h"
@@ -120,6 +122,8 @@ typedef struct _GtkPopoverMenuClass GtkPopoverMenuClass;
 struct _GtkPopoverMenu
 {
   GtkPopover parent_instance;
+
+  GtkWidget *active_item;
 };
 
 struct _GtkPopoverMenuClass
@@ -133,6 +137,25 @@ enum {
 
 G_DEFINE_TYPE (GtkPopoverMenu, gtk_popover_menu, GTK_TYPE_POPOVER)
 
+void
+gtk_popover_menu_set_active_item (GtkPopoverMenu *menu,
+                                  GtkWidget      *item)
+{
+  if (menu->active_item != item)
+    {
+      if (menu->active_item)
+        gtk_widget_unset_state_flags (menu->active_item, GTK_STATE_FLAG_SELECTED);
+
+      menu->active_item = item;
+
+      if (menu->active_item)
+        {
+          gtk_widget_set_state_flags (menu->active_item, GTK_STATE_FLAG_SELECTED, FALSE);
+          gtk_widget_grab_focus (menu->active_item);
+       }
+    }
+}
+
 static void
 visible_submenu_changed (GObject        *object,
                          GParamSpec     *pspec,
diff --git a/gtk/gtkpopovermenuprivate.h b/gtk/gtkpopovermenuprivate.h
new file mode 100644 (file)
index 0000000..024d20c
--- /dev/null
@@ -0,0 +1,30 @@
+/* GTK - The GIMP Toolkit
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_POPOVER_MENU_PRIVATE_H__
+#define __GTK_POPOVER_MENU_PRIVATE_H__
+
+#include "gtkpopovermenu.h"
+
+G_BEGIN_DECLS
+
+void gtk_popover_menu_set_active_item (GtkPopoverMenu *popover,
+                                       GtkWidget      *item);
+
+G_END_DECLS
+
+#endif /* __GTK_POPOVER_PRIVATE_H__ */